// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/parser/parser.h>
#include <cantor/transform/dqls/join_type.h>
#include <cantor/transform/expr.h>
#include <cantor/transform/expr.h>
#include <cantor/transform/exprs/range_var.h>

#include "table_source.h"


namespace cantor::transform {
    void TransJoinNode::resolve_ast(const cantor::parser::JoinNode *node) {
        if (!node) {
            status = turbo::data_loss_error("no expr");
            return;
        }

        /// left
        if (node->left) {
            if (node->left->join_node && node->left->source) {
                status = turbo::already_exists_error("left both have table source and join node");
                return;
            }
            if (node->left->join_node) {
                status = TransJoinNode::resolve_to(node->left->join_node, join_node.mutable_left());
            }
            if (node->left->source) {
                status = TransTableSource::resolve_to(node->left->source, join_node.mutable_left());
            }
        }
        if (!status.ok()) {
            return;
        }
        /// right
        if (node->right) {
            if (node->right->join_node && node->right->source) {
                status = turbo::already_exists_error("right both have table source and join node");
                return;
            }
            if (node->right->join_node) {
                status = TransJoinNode::resolve_to(node->right->join_node, join_node.mutable_left());
            }
            if (node->right->source) {
                status = TransTableSource::resolve_to(node->right->source, join_node.mutable_left());
            }
        }
        if (!status.ok()) {
            return;
        }

        /// join_type
        join_node.set_join_type(node->join_type);
        /// expr
        if (node->expr) {
            status = TransExpr::resolve_to(node->expr, join_node.mutable_expr());
        }
        if (!status.ok()) {
            return;
        }

        /// using_col
        auto cols = join_node.mutable_using_col();
        cols->Reserve(node->using_col.size());
        for (size_t i = 0; i < node->using_col.size(); ++i) {
            status = TransExpr::resolve_to(node->using_col[i], join_node.mutable_expr());
            if (!status.ok()) {
                return;
            }
        }

        join_node.set_is_natural(node->is_natural);
        join_node.set_is_straight(node->is_straight);

        set_proto_location(node, join_node.mutable_location());
        set_proto_status(join_node.mutable_status());
    }

    const turbo::Status &TransJoinNode::check() {
        if (!status.ok()) {
            return status;
        }
        if (!join_node.has_location()) {
            make_missing_required_field("location");
            return status;
        }
        return status;
    }

    turbo::Status TransJoinNode::resolve_to(turbo::Nonnull<const cantor::parser::JoinNode *> node, turbo::Nonnull<pb::ast::JoinNode *> out) {
        TransJoinNode trans;
        trans.resolve_ast(node);
        auto status = trans.check();
        if (!status.ok()) {
            return status;
        }
        *out = std::move(trans.join_node);
        return status;
    }

    turbo::Status TransJoinNode::resolve_to(turbo::Nonnull<const cantor::parser::JoinNode *> node, turbo::Nonnull<pb::ast::TableRef *> out) {
        return resolve_to(node, out->mutable_join_node());
    }
} // namespace cantor::transform
